import os
import sys
import json
import pandas as pd
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import pyqtSlot
import datetime
import tushare as ts  # 金融接口

from model.pandasModel import PandasModel
from view.Ui_mainWindow import Ui_MainWindow
from controller.sys_config import SysConfig
from controller.plate_config import PlateConfig
from controller.candlesbarchart import CandlesBarChart
from controller.kline_plot import KLineWidget
from controller.kline_plot import GetData


# 通过ts金融接口获取股票数据df
def getStockData():
    pro = ts.pro_api()
    data = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,market,cnspell')
    today = datetime.datetime.today().strftime('%Y%m%d')  # 今天日期
    df = pro.daily(trade_date=today)  # 今日数据
    if df.empty:  # 今日无数据则用昨日数据
        yesterday = (datetime.date.today() + datetime.timedelta(-1)).strftime('%Y%m%d')
        df = pro.daily(trade_date=yesterday)
    if df.empty:  # 昨日也无数据则用前日数据
        someday = (datetime.date.today() + datetime.timedelta(-2)).strftime('%Y%m%d')
        df = pro.daily(trade_date=someday)
    if df.empty:  # 特殊数据
        specialday = (datetime.date.today() + datetime.timedelta(-3)).strftime('%Y%m%d')
        df = pro.daily(trade_date=specialday)
    # 初始化表格数据
    stockData = pd.merge(data, df)[
        ['ts_code', 'symbol', 'name', 'pct_chg', 'close', 'change', 'open', 'high', 'low', 'vol', 'amount', 'market', 'cnspell']
    ].sort_index()
    stockData['cnspell'] = stockData['cnspell'].str.upper()  # 将拼音缩写变为大写
    return stockData


# 从文件中读取数据,如果文件不存在或是非最新数据,则会更新数据,并写入文件
def initData(filePath):
    if os.path.exists(filePath):  # 文件存在,则判断是否是最新数据
        mtime = datetime.datetime.fromtimestamp(os.path.getmtime(filePath)).strftime("%Y%m%d")  # 文件上次修改日期
        today = datetime.datetime.today().strftime('%Y%m%d')  # 今天日期
        if mtime == today:  # 是最新数据
            # 如果不设置dtype=str,会将数字字符串将数字读,自动删除前面的0, 比如'000001'变成1
            # 但是如果设置了又会有别的BUG,此处用另一个解决方案
            stockData = pd.read_csv(filePath)
            # 重新单独读取symbol那一列,并指定dtype=str
            stockData['symbol'] = pd.read_csv(filePath, usecols=[1], dtype=str)
        else:  # 非最新数据,需要更新数据,并写入文件
            stockData = getStockData()
            stockData.to_csv(filePath, encoding="utf_8_sig", index=False)
    else:  # 文件不存在或今天还没更新,则生成数据,并写入文件
        stockData = getStockData()
        stockData.to_csv(filePath, encoding="utf_8_sig", index=False)
    return stockData


class MainWindow(QMainWindow, Ui_MainWindow):  # 多重继承QMainWindow和Ui_MainWindow
    def __init__(self):
        super(MainWindow, self).__init__()  # 先调用父类QMainWindow的初始化方法
        self.setupUi(self)  # 再调用setupUi方法

        # 表格样式
        self.stockView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 列宽自适应充满表格
        font = QFont('微软雅黑', 11)
        font.setBold(True)
        self.stockView.horizontalHeader().setFont(font)  # 设置表头字体
        self.stockView.setFont(QFont('微软雅黑', 8))  # 设置表身字体
        self.stockView.setEditTriggers(QAbstractItemView.NoEditTriggers)  # 设置表格不可更改
        self.stockView.setSelectionBehavior(QAbstractItemView.SelectRows)  # 设置不可选择单个单元格,只可选择一行
        self.stockView.setSelectionMode(QAbstractItemView.SingleSelection)  # 设置为只能选择单行,不多同时选择多行
        self.stockView.verticalHeader().setDefaultSectionSize(25)  # 设置默认行高
        # self.stockView.horizontalHeader().setDefaultSectionSize(200)  # 设置默认列宽
        self.stockView.setShowGrid(True)  # 设置有网格

        self.stockData = initData("data/stockData.csv")  # 初始化数据
        # 初始化常规板块数据
        self.plate1 = self.stockData[self.stockData['market'] == "主板"]
        self.plate2 = self.stockData[self.stockData['market'] == "中小板"]
        self.plate3 = self.stockData[self.stockData['market'] == "创业板"]
        self.plate4 = self.stockData[self.stockData['market'] == "科创板"]
        self.plate5 = pd.DataFrame()  # 自选股的初始化比较复杂,点击时候再写入数据

        # 表格初始化显示股票列表
        self.headers = ['TS代码', '股市代码', '名称', '涨幅%', '收盘', '涨跌', '开盘', '最高价', '最低价', '成交量(手)', '成交额', '市场', '拼音缩写']
        self.model = PandasModel(self.plate1, self.headers)  # 默认展示主板界面
        self.stockView.setModel(self.model)  # 关联QTableView控件和Model
        self.setBtnStyle(self.plate1Btn)
        # self.stockView.setColumnHidden(self.stockData.columns.get_loc('ts_code'), True)  # 隐藏 ts_code 列
        self.stockView.setColumnHidden(self.stockData.columns.get_loc('market'), True)  # 隐藏 market 列
        self.stockView.setColumnHidden(self.stockData.columns.get_loc('cnspell'), True)  # 隐藏 cnspell 列

        # 设置表格双击事件
        self.stockView.doubleClicked.connect(self.stockView_double_clicked)

        # 隐藏TabWidget的标签标题
        self.stockWidget.tabBar().hide()  # 隐藏stockWidget的标签标题
        self.trainWidget.tabBar().hide()  # 隐藏trainWidget的标签标题
        # 为【股票】的【技术分析】创建一个新的Tab页
        self.wg = CandlesBarChart(self)  # 创建一个K线图表对象self.wg
        self.wg.extInit()  # 使得查询按钮不可见
        self.stockWidget.addTab(self.wg, "K线图")
        # 为【训练】的【获取数据】创建一个新的Tab页
        self.wg2 = CandlesBarChart(self)
        self.wg2.extInit()
        self.wg2.btnReture.setVisible(False)  # 【训练】无返回按钮

        self.welcomeLabel.setStyleSheet("background-color:black;color:white")  # "欢迎使用训练功能。" 的样式

        # 默认进入【股票】页面
        self.mainWidget.setCurrentIndex(0)

    # 【菜单栏】的事件 ######################################################
    @pyqtSlot()
    def on_actSysConfig_triggered(self):
        QMessageBox.information(self, '提示信息', '系统配置功能。')

    @pyqtSlot()
    def on_actSysConfig_triggered(self):
        w = SysConfig()
        w.exec_()

    @pyqtSlot()
    def on_actPlateConfig_triggered(self):
        w = PlateConfig()
        w.exec_()

    @pyqtSlot()
    def on_actExit_triggered(self):
        self.close()

    # 【股票】标签页面的事件 ######################################################
    @pyqtSlot()
    def on_btnTech_clicked(self):  # 技术分析
        index = self.stockView.currentIndex()  # 当前选中的行的索引
        table_row = index.row()  # 根据索引获取行序号
        if table_row == -1:
            return
        ts_code = index.model().index(table_row, 0).data()
        self.wg.stockQuery(ts_code)
        self.stockWidget.setCurrentIndex(1)  # 切换标签

    # 表格行的双击事件
    def stockView_double_clicked(self, index):
        table_row = index.row()  # 点击的表格行
        if table_row == -1:
            return
        ts_code = index.model().index(table_row, 0).data()
        self.wg.stockQuery(ts_code)
        self.stockWidget.setCurrentIndex(1)  # 切换标签

    # 搜索功能
    def on_search(self, str):
        searchText = self.searchInput.text()
        if self.find(searchText, self.stockData.columns.get_loc('ts_code')):
            pass
        elif self.find(searchText, self.stockData.columns.get_loc('name')):
            pass
        elif self.find(searchText, self.stockData.columns.get_loc('cnspell')):
            pass

    # 在column列搜索text文本
    def find(self, text, column=0):
        model = self.stockView.model()
        start = model.index(0, column)
        matches = model.match(
            start, QtCore.Qt.DisplayRole,
            text, 1, QtCore.Qt.MatchContains  # 搜索模式为包含
        )
        if matches:
            index = matches[0]  # index.row(), index.column()
            self.stockView.selectRow(index.row())
            return True
        return False

    # 设置选中某个板块时,按钮的样式
    def setBtnStyle(self, plateBtn):
        self.plate1Btn.setEnabled(True)
        self.plate2Btn.setEnabled(True)
        self.plate3Btn.setEnabled(True)
        self.plate4Btn.setEnabled(True)
        self.plate5Btn.setEnabled(True)
        self.plate6Btn.setEnabled(True)
        plateBtn.setEnabled(False)

    @pyqtSlot()
    def on_plate1Btn_clicked(self):  # 主板
        self.model = PandasModel(self.plate1, self.headers)
        self.stockView.setModel(self.model)
        self.setBtnStyle(self.plate1Btn)

    @pyqtSlot()
    def on_plate2Btn_clicked(self):  # 中小板
        self.model = PandasModel(self.plate2, self.headers)
        self.stockView.setModel(self.model)
        self.setBtnStyle(self.plate2Btn)

    @pyqtSlot()
    def on_plate3Btn_clicked(self):  # 创业板
        self.model = PandasModel(self.plate3, self.headers)
        self.stockView.setModel(self.model)
        self.setBtnStyle(self.plate3Btn)

    @pyqtSlot()
    def on_plate4Btn_clicked(self):  # 科创板
        self.model = PandasModel(self.plate4, self.headers)
        self.stockView.setModel(self.model)
        self.setBtnStyle(self.plate4Btn)

    @pyqtSlot()
    def on_plate5Btn_clicked(self):
        self.plate5.drop(self.plate5.index, inplace=True)  # 清空数据
        # 初始化自选股板块
        jsonFilePath = 'data/plate.json'
        with open(jsonFilePath, 'r', encoding='utf-8') as f:
            json_data = json.loads(f.read())
        for item in json_data[0]['stocks']:
            self.plate5 = self.plate5.append(self.stockData[self.stockData['symbol'] == item[:6]])
        self.model = PandasModel(self.plate5, self.headers)
        self.stockView.setModel(self.model)
        self.setBtnStyle(self.plate5Btn)

    # 【训练】标签页面的事件 ######################################################
    @pyqtSlot()
    def on_t_dataButton_clicked(self):  # 训练页面, 获取数据
        t_code = self.t_stockCode.text()
        if t_code != "":
            self.wg2.stockQuery(t_code)
            self.trainShowWidget.setCurrentIndex(1)  # 切换标签

    # 公共方法 ######################################################
    def returnToList(self):
        self.stockWidget.setCurrentIndex(0)

    # 处理关闭时的提示
    def closeEvent(self, event):
        reply = QMessageBox.question(self, '退出', "确定要退出吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle(QStyleFactory.create("Fusion"))
    ui = MainWindow()
    ui.show()
    sys.exit(app.exec_())
